﻿/* Copyright (c) 2011-2012, Zetatron Consulting
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

* Neither the name of Zetatron Consulting nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AionCxxLibrary.Packets;
using WorldServer.Services;
using AionCxxLibrary.Models;
using AionCxxLibrary.DAO;
using AionCxxLibrary.Enums;
using WorldServer.Models;
using AionCxxLibrary.Utilities;
using WorldServer.Data;
using WorldServer.Data.Templates;

namespace WorldServer.Packets.Server
{
    public class CharacterPacket
    {
        public static void WriteAppearance(AionServerPacket pkt, TCharacter c)
        {
            pkt.WriteInt(c.Appearance.RGB_Skin);
            pkt.WriteInt(c.Appearance.RGB_Hair);
            pkt.WriteInt(c.Appearance.RGB_Eye);
            pkt.WriteInt(c.Appearance.RGB_Lip);

            pkt.WriteByte(c.Appearance.Face);
            pkt.WriteByte(c.Appearance.Hair);
            pkt.WriteByte(c.Appearance.Decoration);
            pkt.WriteByte(c.Appearance.Tattoo);
            pkt.WriteByte(c.Appearance.FaceContour);
            pkt.WriteByte(c.Appearance.Expression);
            pkt.WriteByte(6);
            pkt.WriteByte(c.Appearance.Jawline);
            pkt.WriteByte(c.Appearance.Forehead);
            pkt.WriteByte(c.Appearance.EyeHeight);
            pkt.WriteByte(c.Appearance.EyeSpace);
            pkt.WriteByte(c.Appearance.EyeWidth);
            pkt.WriteByte(c.Appearance.EyeSize);
            pkt.WriteByte(c.Appearance.EyeShape);
            pkt.WriteByte(c.Appearance.EyeAngle);
            pkt.WriteByte(c.Appearance.BrowHeight);
            pkt.WriteByte(c.Appearance.BrowAngle);
            pkt.WriteByte(c.Appearance.BrowShape);
            pkt.WriteByte(c.Appearance.Nose);
            pkt.WriteByte(c.Appearance.NoseBridge);
            pkt.WriteByte(c.Appearance.NoseWidth);
            pkt.WriteByte(c.Appearance.NoseTip);
            pkt.WriteByte(c.Appearance.Cheek);
            pkt.WriteByte(c.Appearance.LipHeight);
            pkt.WriteByte(c.Appearance.MouthSize);
            pkt.WriteByte(c.Appearance.LipSize);
            pkt.WriteByte(c.Appearance.Smile);
            pkt.WriteByte(c.Appearance.LipShape);
            pkt.WriteByte(c.Appearance.JawHeight);
            pkt.WriteByte(c.Appearance.ChinJut);
            pkt.WriteByte(c.Appearance.EarShape);
            pkt.WriteByte(c.Appearance.HeadSize);
            pkt.WriteByte(c.Appearance.Neck);
            pkt.WriteByte(c.Appearance.NeckLength);
            pkt.WriteByte(c.Appearance.ShoulderSize);
            pkt.WriteByte(c.Appearance.Torso);
            pkt.WriteByte(c.Appearance.Chest);
            pkt.WriteByte(c.Appearance.Waist);
            pkt.WriteByte(c.Appearance.Hips);
            pkt.WriteByte(c.Appearance.ArmThickness);
            pkt.WriteByte(c.Appearance.HandSize);
            pkt.WriteByte(c.Appearance.LegThickness);
            pkt.WriteByte(c.Appearance.LegLength);
            pkt.WriteByte(c.Appearance.FacialRate);
            pkt.WriteByte(0);
            pkt.WriteByte(c.Appearance.ArmLength);
            pkt.WriteByte(c.Appearance.FeetSize);
            pkt.WriteByte(c.Appearance.Shoulders);
            pkt.WriteByte(c.Appearance.FaceShape);
        }
        public static void WriteCharacterTemplate(AionServerPacket pkt, TCharacter c)
        {
            pkt.WriteInt(c.ID);
            pkt.WriteString(c.Name);
            pkt.WriteBytes(new byte[50 - (c.Name.Length * 2)]);

            int gender = c.Gender;
            int race = (int)c.Race;
            int clazz = (int)c.Class;

            pkt.WriteInt(gender);
            pkt.WriteInt(race);
            pkt.WriteInt(clazz);
            // appearance
            pkt.WriteInt(c.Appearance.Voice);

            WriteAppearance(pkt, c);

            pkt.WriteByte(0); // May be accesslevel
            pkt.WriteShort(0); // unk
            pkt.WriteFloat(c.Appearance.Height);

            int racesex = 100000 + (race*2) + c.Gender;
            pkt.WriteInt(racesex);
            pkt.WriteInt(c.WorldID);
            pkt.WriteVector(c.Position);

            pkt.WriteInt(c.Level);
            pkt.WriteInt(c.Title);

            pkt.WriteBytes(new byte[92]); // Legion Info

            int itemLength = 0;
            foreach (TItem item in c.Equipment)
            {
                if (itemLength >= 208)
                    break;
                ItemTemplate template = Datastore.ItemsById[item.ItemID];
                if (template.ArmorType != ArmorType.NoArmor && template.WeaponType != WeaponType.NoWeapon)
                    continue;
                pkt.WriteByte(1);
                pkt.WriteInt(item.ItemID);
                pkt.WriteInt(0); // Should be godstone template id
                pkt.WriteInt(item.ItemColor);
                itemLength += 13;
            }

            pkt.WriteBytes(new byte[208 - itemLength]);
            pkt.WriteInt(0); // Deletion Time
            pkt.WriteInt(0); // 10005 = Hide helmets
        }
    }

    public class SPkt_CharacterCreate : AionServerPacket
    {
        TCharacter character;
        CharacterCreateResponse response;
        public SPkt_CharacterCreate(TCharacter c, CharacterCreateResponse r)
        {
            character = c;
            response = r;
        }
        public override void WritePacket()
        {
            WriteInt((int)response);
            if(response == CharacterCreateResponse.Success)
                CharacterPacket.WriteCharacterTemplate(this, character);
            else
                WriteBytes(new byte[512]);
        }
    }

    public class SPkt_CharacterList : AionServerPacket
    {
        public override void WritePacket()
        {
            WorldConnection wc = (WorldConnection)Connection;
            WriteInt(wc.Keys[2]); // KeyWorld2

            // get list of character template

            List<TCharacter> chars = CharacterService.LoadCharactersForAccount(wc.account.ID);
            WriteByte((byte)chars.Count);

            foreach (TCharacter character in chars)
            {
                CharacterPacket.WriteCharacterTemplate(this, character);
                WriteShort(0); // unk
                WriteInt(0); // unk
                WriteInt(0); // Unread Mail Count
                WriteLong(0); // unk

                WriteInt(0); // Broker Items sold
                WriteLong(0); // Broker Kinah to gather
                WriteBytes(new byte[12]);
            }
        }
    }

    public class SPkt_CharacterCheckName : AionServerPacket
    {
        CharacterCreateResponse response;
        public SPkt_CharacterCheckName(CharacterCreateResponse resp)
        {
            response = resp;
        }
        public override void  WritePacket()
        {
 	        WriteByte((byte)(int)(response));
        }
    }

    public class SPkt_CharacterStats : AionServerPacket
    {
        public override void WritePacket()
        {
            WorldConnection wc = (WorldConnection)Connection;
            Character c = wc.Character;

            WriteInt(c.GetGUID());
            WriteInt((int)(Utility.DateTimeToTimestamp(DateTime.Now) / 10000)); // Atreia Time

            WriteUnsignedShort((ushort)c.StatsList.GetStat(Stat.Attack));
            WriteUnsignedShort((ushort)c.StatsList.GetStat(Stat.Health));
            WriteUnsignedShort((ushort)c.StatsList.GetStat(Stat.Accuracy));
            WriteUnsignedShort((ushort)c.StatsList.GetStat(Stat.Agility));
            WriteUnsignedShort((ushort)c.StatsList.GetStat(Stat.Knowledge));
            WriteUnsignedShort((ushort)c.StatsList.GetStat(Stat.Will));

            WriteUnsignedShort((ushort)c.StatsList.GetStat(Stat.ResistanceWater));
            WriteUnsignedShort((ushort)c.StatsList.GetStat(Stat.ResistanceWind));
            WriteUnsignedShort((ushort)c.StatsList.GetStat(Stat.ResistanceEarth));
            WriteUnsignedShort((ushort)c.StatsList.GetStat(Stat.ResistanceFire));
            WriteUnsignedShort((ushort)c.StatsList.GetStat(Stat.ElementalResistanceLight));
            WriteUnsignedShort((ushort)c.StatsList.GetStat(Stat.ElementalResistanceDark));

            WriteShort(c.Template.Level);

            WriteInt(0); WriteShort(0);

            WriteLong(1400 - c.Template.Experience);
            WriteLong(c.Template.ExperienceRecoverable);
            WriteLong(c.Template.Experience);
            
            WriteInt(0);
            WriteInt((int)c.StatsList.GetStat(Stat.MaxHp));
            WriteInt(c.StatsList.GetHealth());

            WriteInt((int)c.StatsList.GetStat(Stat.MaxMp));
            WriteInt(c.StatsList.GetMana());

            WriteUnsignedShort((ushort)c.StatsList.GetStat(Stat.MaxDp));
            WriteUnsignedShort(c.StatsList.GetDivinePoints());

            WriteInt((int)c.StatsList.GetStat(Stat.MaxFp));
            WriteInt(c.StatsList.GetFlyPoints());

            WriteShort(0); // Fly State

            WriteUnsignedShort((ushort)c.StatsList.GetStat(Stat.MainHandAttack));
            WriteUnsignedShort((ushort)c.StatsList.GetStat(Stat.OffHandAttack));
            WriteUnsignedShort((ushort)c.StatsList.GetStat(Stat.PhysicalDefend));
            WriteUnsignedShort((ushort)c.StatsList.GetStat(Stat.MagicalAttack));
            WriteUnsignedShort((ushort)c.StatsList.GetStat(Stat.MagicalResist));

            WriteFloat(c.StatsList.GetStat(Stat.AttackRange));
            WriteUnsignedShort((ushort)(c.StatsList.GetStat(Stat.AttackDelay) * 1000));

            WriteUnsignedShort((ushort)c.StatsList.GetStat(Stat.Dodge));
            WriteUnsignedShort((ushort)c.StatsList.GetStat(Stat.Parry));
            WriteUnsignedShort((ushort)c.StatsList.GetStat(Stat.Block));
            WriteUnsignedShort((ushort)c.StatsList.GetStat(Stat.MainHandCritical));
            WriteUnsignedShort((ushort)c.StatsList.GetStat(Stat.OffHandCritical));
            WriteUnsignedShort((ushort)c.StatsList.GetStat(Stat.MainHandAccuracy));
            WriteUnsignedShort((ushort)c.StatsList.GetStat(Stat.OffHandAccuracy));
            WriteUnsignedShort((ushort)c.StatsList.GetStat(Stat.BoostCastingTime));
            WriteUnsignedShort((ushort)c.StatsList.GetStat(Stat.MagicalAccuracy));
            WriteUnsignedShort((ushort)c.StatsList.GetStat(Stat.MagicalCritical));
            WriteUnsignedShort(0);
            
            WriteFloat((c.StatsList.GetStat(Stat.BoostCastingTime) / 100f) + 1);
            WriteUnsignedShort((ushort)c.StatsList.GetStat(Stat.Concentration));

            WriteUnsignedShort((ushort)c.StatsList.GetStat(Stat.BoostMagicalSkill));
            WriteUnsignedShort((ushort)c.StatsList.GetStat(Stat.BoostHeal));
            WriteUnsignedShort((ushort)c.StatsList.GetStat(Stat.PhysicalCriticalResist));
            WriteUnsignedShort((ushort)c.StatsList.GetStat(Stat.MagicalCriticalResist));
            WriteUnsignedShort((ushort)c.StatsList.GetStat(Stat.PhysicalCriticalDamageReduce));
            WriteUnsignedShort((ushort)c.StatsList.GetStat(Stat.MagicalCriticalDamageReduce));
            WriteUnsignedShort(20511);

            WriteInt(c.Inventory.Size); // Inventory Mixed Cube Size
            WriteInt(c.Inventory.Count); // Inventory Size

            WriteLong(0);
            WriteInt((int)c.Template.Class);
            WriteLong(0);

            WriteLong(0); // Energy of Repose
            WriteLong(0); 
            
            WriteLong(0); // unk

            WriteUnsignedShort((ushort)c.StatsList.GetInitialStat(Stat.Attack));
            WriteUnsignedShort((ushort)c.StatsList.GetInitialStat(Stat.Health));
            WriteUnsignedShort((ushort)c.StatsList.GetInitialStat(Stat.Accuracy));
            WriteUnsignedShort((ushort)c.StatsList.GetInitialStat(Stat.Agility));
            WriteUnsignedShort((ushort)c.StatsList.GetInitialStat(Stat.Knowledge));
            WriteUnsignedShort((ushort)c.StatsList.GetInitialStat(Stat.Will));
            WriteUnsignedShort((ushort)c.StatsList.GetInitialStat(Stat.ResistanceWater));
            WriteUnsignedShort((ushort)c.StatsList.GetInitialStat(Stat.ResistanceWind));
            WriteUnsignedShort((ushort)c.StatsList.GetInitialStat(Stat.ResistanceEarth));
            WriteUnsignedShort((ushort)c.StatsList.GetInitialStat(Stat.ResistanceFire));
            WriteUnsignedShort((ushort)c.StatsList.GetInitialStat(Stat.ElementalResistanceLight));
            WriteUnsignedShort((ushort)c.StatsList.GetInitialStat(Stat.ElementalResistanceDark));

            WriteInt((int)c.StatsList.GetInitialStat(Stat.MaxHp));
            WriteInt((int)c.StatsList.GetInitialStat(Stat.MaxMp));
            WriteInt((int)c.StatsList.GetInitialStat(Stat.MaxDp));
            WriteInt((int)c.StatsList.GetInitialStat(Stat.MaxFp));
            WriteUnsignedShort((ushort)c.StatsList.GetInitialStat(Stat.MainHandAttack));
            WriteUnsignedShort((ushort)c.StatsList.GetInitialStat(Stat.OffHandAttack));
            WriteUnsignedShort((ushort)c.StatsList.GetInitialStat(Stat.MagicalAttack));
            WriteUnsignedShort((ushort)c.StatsList.GetInitialStat(Stat.PhysicalDefend));
            WriteUnsignedShort((ushort)c.StatsList.GetInitialStat(Stat.MagicalResist));
            WriteUnsignedShort(0);
            WriteFloat(c.StatsList.GetStat(Stat.AttackRange));
            WriteUnsignedShort((ushort)c.StatsList.GetInitialStat(Stat.Dodge));
            WriteUnsignedShort((ushort)c.StatsList.GetInitialStat(Stat.Parry));
            WriteUnsignedShort((ushort)c.StatsList.GetInitialStat(Stat.Block));
            WriteUnsignedShort((ushort)c.StatsList.GetInitialStat(Stat.MainHandCritical));
            WriteUnsignedShort((ushort)c.StatsList.GetInitialStat(Stat.OffHandCritical));
            WriteUnsignedShort((ushort)c.StatsList.GetInitialStat(Stat.MagicalCritical));
            WriteUnsignedShort(0);
            WriteUnsignedShort((ushort)c.StatsList.GetInitialStat(Stat.MainHandAccuracy));
            WriteUnsignedShort((ushort)c.StatsList.GetInitialStat(Stat.OffHandAccuracy));
            WriteUnsignedShort(0); // Base Casting Speed
            WriteUnsignedShort((ushort)c.StatsList.GetInitialStat(Stat.MagicalAccuracy));
            WriteUnsignedShort((ushort)c.StatsList.GetInitialStat(Stat.Concentration));
            WriteUnsignedShort((ushort)c.StatsList.GetInitialStat(Stat.BoostMagicalSkill));
            WriteUnsignedShort((ushort)c.StatsList.GetInitialStat(Stat.BoostHeal));
            WriteUnsignedShort((ushort)c.StatsList.GetInitialStat(Stat.PhysicalCriticalResist));
            WriteUnsignedShort((ushort)c.StatsList.GetInitialStat(Stat.MagicalCriticalResist));
            WriteUnsignedShort((ushort)c.StatsList.GetInitialStat(Stat.PhysicalCriticalDamageReduce));
            WriteUnsignedShort((ushort)c.StatsList.GetInitialStat(Stat.MagicalCriticalDamageReduce));

        }
    }

    public class SPkt_CharacterDetails : AionServerPacket
    {
        Character c;
        public SPkt_CharacterDetails(Character c)
        {
            this.c = c;
        }

        public override void WritePacket()
        {
            WriteFloat(c.Position.x);
            WriteFloat(c.Position.y);
            WriteFloat(c.Position.z);
            WriteInt(c.GetGUID());

            int raceid = (int)c.Template.Race;
            int classid = (int)c.Template.Class;
            int racesex = 100000 + (raceid * 2) + c.Template.Gender;

            WriteInt(racesex);
            WriteInt(racesex); // Transformed Model ID
            WriteByte(0);
            WriteByte(0); // 2 to enable Skills under Transformation
            WriteShort(0);
            WriteByte(0);
            WriteByte((byte)(((WorldConnection)Connection).Character.IsEnemy(c) ? 0x00 : 0x26));

            WriteByte((byte)raceid);
            WriteByte((byte)classid);
            WriteByte(c.Template.Gender);

            WriteShort(1); // State
            WriteBytes(new byte[8]); // unknown

            WriteByte(c.Position.h);

            WriteString(c.Template.Name);

            WriteUnsignedShort(c.Template.Title);
            WriteShort(0); // Stuck on ground = 1
            WriteShort(0); // CastingSkillID

            WriteInt(0); // LegionID
            WriteByte(0); // LegionEmblemVersion
            WriteShort(0);
            WriteByte(0); // EmblemRed
            WriteByte(0); // EmblemGreen
            WriteByte(0); // EmblemBlue
            WriteString(""); // Legion Name

            WriteByte((byte)c.StatsList.GetHealthPercent());
            WriteUnsignedShort(c.StatsList.GetDivinePoints());
            WriteByte(0);

            int mask = 0;
            List<Item> Equipment = c.Inventory.FindAll(i => i.Equipped && i.Slot != (int)EquipmentSlot.OffMain && i.Slot != (int)EquipmentSlot.OffSub);
            Equipment.Sort(new Comparison<Item>((i1, i2) =>
            {
                if (i1.Slot > i2.Slot)
                    return 1;
                else if (i2.Slot > i1.Slot)
                    return -1;
                else
                    return 0;
            }));
            foreach(Item item in Equipment)
            {
                mask |= item.Slot;
            }
            
            WriteUnsignedShort((ushort)mask);

            foreach(Item item in Equipment)
            {
                WriteInt(item.SkinID == 0 ? item.TemplateID : item.SkinID);
                WriteInt(0); // Godstone Item ID
                WriteInt(item.Color);
                WriteShort(0);
            }

            CharacterPacket.WriteAppearance(this, c.Template);

            WriteShort(0);

            WriteFloat(c.Template.Appearance.Height);
            WriteFloat(0.25f); // Scale
            WriteFloat(2f); // Gravity
            WriteFloat(c.StatsList.GetStat(Stat.Speed));
            
            WriteUnsignedShort((ushort)(c.StatsList.GetInitialStat(Stat.AttackDelay) * 1000));
            WriteUnsignedShort((ushort)(c.StatsList.GetStat(Stat.AttackDelay) * 1000));
            WriteByte(2);

            WriteString(""); // Player Store Message

            WriteFloat(0); WriteFloat(0); WriteFloat(0);
            WriteFloat(c.Position.x);
            WriteFloat(c.Position.y);
            WriteFloat(c.Position.z);
            WriteByte(0); // Move Type

            WriteByte(0); // Visual State
            WriteString("Xitaaa"); // Note

            WriteShort(c.Template.Level);
            WriteShort(0); // Display Permission
            WriteShort(0); // Deny permission
            WriteShort(0); // Abyss Rank
            WriteShort(0);
            WriteInt(0); // TargetAionID

            WriteBytes(new byte[8]);
        }

    }

    public class SPkt_ReturnLocation : AionServerPacket
    {
        public override void WritePacket()
        {
            WriteByte(0); // 0x04 = Kisk
            WriteByte(0);
            WriteInt(220010000); // WorldID
            WriteFloat(571.038818359375f);
            WriteFloat(2787.342041015625f);
            WriteFloat(299.875f);
            WriteInt(0); // Kisk Object ID
        }
    }

    public class SPkt_CharacterDelete : AionServerPacket
    {
        int charId;
        public SPkt_CharacterDelete(int charId)
        {
            this.charId = charId;
        }
        public override void WritePacket()
        {
            WriteInt(0);
            WriteInt(charId);
            WriteInt(1);
        }
    }

    public class SPkt_CharacterAppearance : AionServerPacket
    {
        public Character c;
        public SPkt_CharacterAppearance(Character c)
        {
            this.c = c;
        }

        public override void WritePacket()
        {
            WriteInt(c.GetGUID());

            int mask = 0;
            List<Item> Equipment = c.Inventory.FindAll(i => i.Equipped && i.Slot != (int)EquipmentSlot.OffMain && i.Slot != (int)EquipmentSlot.OffSub);
            Equipment.Sort(new Comparison<Item>((i1,i2) => {
                if(i1.Slot > i2.Slot)
                    return 1;
                else if(i2.Slot > i1.Slot)
                    return -1;
                else
                    return 0;
            }));

            foreach (Item item in Equipment)
            {
                mask |= item.Slot;
            }

            WriteUnsignedShort((ushort)mask);

            foreach (Item item in Equipment)
            {
                WriteInt(item.SkinID == 0 ? item.TemplateID : item.SkinID);
                WriteInt(0); // Godstone Item ID
                WriteInt(item.Color);
                WriteShort(0);
            }
        }
    }

}
